﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Xml;
using UnityEditor;
using UnityEditor.Build.Reporting;
using UnityEngine;
using Debug = UnityEngine.Debug;
using Object = UnityEngine.Object;

namespace XUBuilder
{
    namespace Editor
    {
        internal class XUBuilderAndroid
        {
            public static void Build(XUBuilderAndroidSetting.Item item)
            {
                GenOutputConfig(item);
                SetPlayerSettings(item);
                ReplacePlugins(item);
                ReplacTargetSdkVersion(item);
                SetIconAndSplash(item);
                AssetDatabase.SaveAssets();
                AssetDatabase.Refresh();
                if (!item.dontBuild)
                {
                    BuildApk(item);
                    ClearIconAndSplash(item);
                    AssetDatabase.SaveAssets();
                    AssetDatabase.Refresh();
                }
            }

            private static void GenOutputConfig(XUBuilderAndroidSetting.Item item)
            {
                OutputConfig outputConfig = new OutputConfig(item.channel, item.package);
                string outputConfigJson = JsonUtility.ToJson(outputConfig, true);
                string savePath = Application.streamingAssetsPath + "/OutputConfig.txt";
                XUBuilderUtil.EnsureFolder(Path.GetDirectoryName(savePath));
                File.WriteAllText(savePath, outputConfigJson);
                Debug.Log(item.gameName + " 生成配置");
            }

            private static void SetPlayerSettings(XUBuilderAndroidSetting.Item item)
            {
                PlayerSettings.productName = item.gameName;
                PlayerSettings.applicationIdentifier = item.packageName;
                PlayerSettings.bundleVersion = item.bundleVersion;
                PlayerSettings.Android.bundleVersionCode = (int)item.bundleVersionCode;
                PlayerSettings.Android.useCustomKeystore = true;
                PlayerSettings.Android.keystoreName = item.GetKeystorePath() + "/" + item.keystoreName + ".keystore";
                PlayerSettings.Android.keystorePass = item.keystorePass;
                PlayerSettings.Android.keyaliasName = item.keyaliasName;
                PlayerSettings.Android.keyaliasPass = item.keyaliasPass;
                PlayerSettings.Android.useAPKExpansionFiles = item.splitApplicationBinary;
                Debug.Log(item.gameName + " 设置参数");
            }

            private static void ReplacePlugins(XUBuilderAndroidSetting.Item item)
            {
                string commonDir = GetCommonPath();
                string fromDir = item.GetMainPath();
                string toDir = Application.dataPath + "/Plugins/Android";
                //清空文件夹
                XUBuilderUtil.EnsureFolder(commonDir);
                XUBuilderUtil.EnsureFolder(fromDir);
                XUBuilderUtil.EnsureFolder(toDir);
                XUBuilderUtil.ClearFolder(toDir, false);
                //复制common文件夹
                foreach (var file in Directory.GetFiles(commonDir, "*.*", SearchOption.AllDirectories))
                {
                    string toFile = file.Replace(commonDir, toDir);
                    Directory.CreateDirectory(Path.GetDirectoryName(toFile));
                    File.Copy(file, toFile, true);
                }
                //复制fromDir文件夹
                foreach (var file in Directory.GetFiles(fromDir, "*.*", SearchOption.AllDirectories))
                {
                    string toFile = file.Replace(fromDir, toDir);
                    Directory.CreateDirectory(Path.GetDirectoryName(toFile));
                    File.Copy(file, toFile, true);
                }
                Debug.Log(item.gameName + " 替换Plugins");
            }

            static private void ReplacTargetSdkVersion(XUBuilderAndroidSetting.Item item)
            {
                string manifestPath = Application.dataPath + "/Plugins/Android/AndroidManifest.xml";
                if (File.Exists(manifestPath))
                {
                    //读取AndroidManifest
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.Load(manifestPath);
                    XmlNode xnRoot = xmlDoc.SelectSingleNode("manifest");
                    XmlNode xnSDK = xnRoot.SelectSingleNode("uses-sdk");
                    XmlElement xeSDK = (XmlElement)xnSDK;
                    //修改targetSdkVersion
                    if (xeSDK != null)
                    {
                        string minSdkVersion = xeSDK.GetAttribute("android:minSdkVersion");
                        string targetSdkVersion = xeSDK.GetAttribute("android:targetSdkVersion");
                        PlayerSettings.Android.minSdkVersion = (AndroidSdkVersions)int.Parse(minSdkVersion);
                        PlayerSettings.Android.targetSdkVersion = (AndroidSdkVersions)int.Parse(targetSdkVersion);
                        Debug.Log(item.gameName + " 修改minSdkVersion和targetSdkVersion");
                    }
                    else
                    {
                        Debug.Log(item.gameName + " AndroidManifest.xml未配置uses-sdk");
                    }
                }
                else
                {
                    Debug.Log(item.gameName + " 找不到AndroidManifest.xml");
                }
            }

            private static void SetIconAndSplash(XUBuilderAndroidSetting.Item item)
            {
                //图标
                {
                    string iconFromPath = item.iconPath;
                    if (File.Exists(iconFromPath))
                    {
                        string iconToPath = Application.dataPath + "/TempBuild/" + Path.GetFileName(iconFromPath);
                        string iconAssetPath = iconToPath.Substring(iconToPath.IndexOf("Assets"));
                        //复制图标
                        XUBuilderUtil.EnsureFolder(Path.GetDirectoryName(iconToPath));
                        File.Copy(iconFromPath, iconToPath, true);
                        //导入图标
                        AssetDatabase.ImportAsset(iconAssetPath);
                        Texture2D iconTexture = AssetDatabase.LoadAssetAtPath(iconAssetPath, typeof(Texture2D)) as Texture2D;
                        //设置图标
                        PlayerSettings.SetIconsForTargetGroup(BuildTargetGroup.Unknown, new Texture2D[] { iconTexture });
                    }
                }
                //闪屏
                {
                    string splashFromPath = item.splashPath;
                    if (File.Exists(splashFromPath))
                    {
                        string splashToPath = Application.dataPath + "/TempBuild/" + Path.GetFileName(splashFromPath);
                        string splashAssetPath = splashToPath.Substring(splashToPath.IndexOf("Assets"));
                        //复制闪屏
                        XUBuilderUtil.EnsureFolder(Path.GetDirectoryName(splashToPath));
                        File.Copy(splashFromPath, splashToPath, true);
                        //导入闪屏
                        AssetDatabase.ImportAsset(splashAssetPath);
                        Texture2D splashTexture = AssetDatabase.LoadAssetAtPath(splashAssetPath, typeof(Texture2D)) as Texture2D;
                        //设置闪屏
                        string projectSettingsPath = "ProjectSettings/ProjectSettings.asset";
                        Object projectSettingsObj = AssetDatabase.LoadAllAssetsAtPath(projectSettingsPath)[0];
                        SerializedObject projectSettingsSObj = new SerializedObject(projectSettingsObj);
                        SerializedProperty androidSplashFileId = projectSettingsSObj.FindProperty("androidSplashScreen.m_FileID");
                        if (androidSplashFileId != null)
                        {
                            androidSplashFileId.intValue = splashTexture.GetInstanceID();
                        }
                        projectSettingsSObj.ApplyModifiedProperties();
                    }
                }
                Debug.Log(item.gameName + " 设置图标和闪屏");
            }

            private static void BuildApk(XUBuilderAndroidSetting.Item item)
            {
                //获取首包场景
                List<string> scenes = new List<string>();
                {
                    foreach (EditorBuildSettingsScene scene in EditorBuildSettings.scenes)
                    {
                        if (!scene.enabled)
                        {
                            continue;
                        }
                        scenes.Add(scene.path);
                    }
                }

                //设置打包选项
                BuildOptions buildOptions = BuildOptions.None;
                {
                    buildOptions |= BuildOptions.AcceptExternalModificationsToPlayer;
                    if (item.development)
                    {
                        buildOptions |= BuildOptions.Development;
                    }
                    if (item.connectProfiler)
                    {
                        buildOptions |= BuildOptions.ConnectWithProfiler;
                    }
                    if (item.deepProfilingSupport)
                    {
                        buildOptions |= BuildOptions.EnableDeepProfilingSupport;
                    }
                }

                //构建路径
                string projPath;
                {
                    StringBuilder projPathBuilder = new StringBuilder();
                    projPathBuilder.Append(GetOutputPath());
                    projPathBuilder.Append("/");
                    projPathBuilder.Append(item.gameName);
                    projPathBuilder.Append("_");
                    projPathBuilder.Append(item.package);
                    projPathBuilder.Append("_");
                    projPathBuilder.Append(DateTime.Now.ToString("G").Replace("/", "-").Replace(":", "-").Replace(" ", "-"));
                    projPath = projPathBuilder.ToString();
                    XUBuilderUtil.ClearFolder(projPath, true);
                    Directory.CreateDirectory(projPath);
                }

                //构建AS工程
                {
                    EditorUserBuildSettings.exportAsGoogleAndroidProject = true;
                    BuildReport report = BuildPipeline.BuildPlayer(scenes.ToArray(), projPath, BuildTarget.Android, buildOptions);
                    BuildSummary summary = report.summary;

                    if (summary.result == BuildResult.Succeeded)
                    {
                        StringBuilder stringBuilder = new StringBuilder();
                        stringBuilder.AppendLine("构建成功");
                        stringBuilder.AppendLine("开始：" + summary.buildStartedAt);
                        stringBuilder.AppendLine("结束：" + summary.buildEndedAt);
                        stringBuilder.AppendLine("耗时：" + summary.totalTime);
                        stringBuilder.AppendLine("大小：" + XUBuilderUtil.GetSizeString((long)summary.totalSize));
                        Debug.Log(stringBuilder.ToString());
                    }

                    if (summary.result == BuildResult.Failed)
                    {
                        Debug.LogError("构建失败");
                    }
                }

                //Gradle
                {
                    //解决中文路径问题
                    {
                        string gradlePropertiesPath = projPath + "/gradle.properties";
                        StringBuilder gradleProperties = new StringBuilder(File.ReadAllText(gradlePropertiesPath));
                        gradleProperties.AppendLine();
                        gradleProperties.AppendLine("org.gradle.jvmargs=-Dfile.encoding=UTF-8");
                        gradleProperties.AppendLine("android.overridePathCheck=true");
                        File.WriteAllText(gradlePropertiesPath, gradleProperties.ToString());
                    }
                    //解决StreamingAssets文件夹不压缩问题
                    {
                        string gradleSettingPath = projPath + "/launcher/build.gradle";
                        List<string> gradleSettingLines = new List<string>(File.ReadAllLines(gradleSettingPath));
                        for (int i = 0; i < gradleSettingLines.Count; i++)
                        {
                            if (gradleSettingLines[i].Trim().StartsWith("noCompress"))
                            {
                                gradleSettingLines[i] = "\tnoCompress = ['.unity3d', '.ress', '.resource', '.obb']";
                            }
                        }
                        File.WriteAllLines(gradleSettingPath, gradleSettingLines.ToArray());
                    }
                    //复制Gradlew
                    {
                        string unityEditorPath = System.Threading.Thread.GetDomain().BaseDirectory;
                        string gradleTemplatesPath = unityEditorPath + "/Data/PlaybackEngines/AndroidPlayer/Tools/VisualStudioGradleTemplates";
                        //复制gradlew.bat
                        {
                            string gradlewFilePath = gradleTemplatesPath + "/gradlew.bat";
                            string gradlewFileCopyPath = projPath + "/gradlew.bat";
                            File.Copy(gradlewFilePath, gradlewFileCopyPath);
                        }
                        //复制gradle-wrapper.jar
                        {
                            string gradleWrapperFilePath = gradleTemplatesPath + "/gradle-wrapper.jar";
                            string gradleWrapperFileCopyPath = projPath + "/gradle/wrapper/gradle-wrapper.jar";
                            XUBuilderUtil.EnsureFolder(Path.GetDirectoryName(gradleWrapperFileCopyPath));
                            File.Copy(gradleWrapperFilePath, gradleWrapperFileCopyPath);
                        }
                        //复制gradle-wrapper.properties
                        {
                            string gradleWrapperPropertiesTemplateFilePath = gradleTemplatesPath + "/gradle-wrapper.properties.template";
                            string gradleWrapperPropertiesCopyFilePath = projPath + "/gradle/wrapper/gradle-wrapper.properties";

                            XUBuilderUtil.EnsureFolder(Path.GetDirectoryName(gradleWrapperPropertiesCopyFilePath));

                            //获取Gradle版本
                            string graldeVersion = "5.6.4";
                            {
#if UNITY_2017
                                graldeVersion = "4.6";
#elif UNITY_2018
                                graldeVersion = "5.1.1";
#elif UNITY_2019
                                graldeVersion = "5.6.4";
#elif UNITY_2020
                                graldeVersion = "6.7.1";
#endif
                            }
                            //替换Gradle版本
                            {
                                string propertiesContent = File.ReadAllText(gradleWrapperPropertiesTemplateFilePath).Replace("$(GradleVersion)", graldeVersion);
                                File.WriteAllText(gradleWrapperPropertiesCopyFilePath, propertiesContent);
                            }
                        }
                        //执行构建
                        {
                            //生成bat
                            string buildBatPath = projPath + "/build.bat";
                            {
                                StringBuilder builder = new StringBuilder();
                                builder.AppendLine("gradlew assembleRelease");
                                File.WriteAllText(buildBatPath, builder.ToString());
                            }
                            //执行bat
                            {
                                Process zProcInfo = new Process();
                                zProcInfo.StartInfo.WorkingDirectory = projPath;
                                zProcInfo.StartInfo.FileName = buildBatPath;
                                zProcInfo.StartInfo.CreateNoWindow = false;
                                zProcInfo.Start();
                                zProcInfo.WaitForExit();
                            }
                            //复制apk
                            {
                                string apkFromPath = projPath + "/launcher/build/outputs/apk/release/launcher-release.apk";
                                string apkToPath = projPath + ".apk";
                                if (File.Exists(apkFromPath))
                                {
                                    File.Copy(apkFromPath, apkToPath, true);
                                    Process.Start(Path.GetDirectoryName(apkToPath));
                                }
                                else
                                {
                                    if (EditorUtility.DisplayDialog("错误", "构建APK失败\n可以在 cmd 中重新执行 build 查看具体原因", "打开工程", "关闭"))
                                    {
                                        Process.Start(projPath);
                                    }
                                }
                            }
                        }
                    }
                }
            }

            private static void ClearIconAndSplash(XUBuilderAndroidSetting.Item item)
            {
                string tempPath = Application.dataPath + "/TempBuild";
                XUBuilderUtil.ClearFolder(tempPath, true);
            }

            //=============================================================================================================

            public static string GetInputPath()
            {
                string outputPath = Application.dataPath.Replace("Assets", "/BuildInput/Android");
                XUBuilderUtil.EnsureFolder(outputPath);
                return outputPath;
            }

            public static string GetOutputPath()
            {
                string outputPath = Application.dataPath.Replace("Assets", "/BuildOutput/Android");
                XUBuilderUtil.EnsureFolder(outputPath);
                return outputPath;
            }

            public static string GetCommonPath()
            {
                string outputPath = Application.dataPath.Replace("Assets", "/BuildInput/Android/Common");
                XUBuilderUtil.EnsureFolder(outputPath);
                return outputPath;
            }

            //=============================================================================================================

            public static bool GenKeystore(string folder, string keystoreName, string keyaliasName, string password)
            {
                string keystorePath = folder + "/" + keystoreName + ".keystore";
                StringBuilder builder = new StringBuilder();
                builder.AppendLine("set wshshell=CreateObject(\"Wscript.shell\")");
                builder.AppendLine("wshshell.run \"cmd.exe\"");
                builder.AppendLine("");
                builder.AppendLine("wscript.sleep 1000");
                builder.AppendLine(string.Format("wshshell.sendkeys \"keytool -genkey -alias {0} -keyalg RSA -validity 36500 -keystore \"\"{1}\"\"\"", keyaliasName, keystorePath));
                builder.AppendLine("wshshell.sendkeys \"{enter}\"");
                builder.AppendLine("");
                builder.AppendLine("wscript.sleep 1000");
                builder.AppendLine(string.Format("wshshell.sendkeys \"{0}\"", password));
                builder.AppendLine("wshshell.sendkeys \"{enter}\"");
                builder.AppendLine("");
                builder.AppendLine("wscript.sleep 1000");
                builder.AppendLine(string.Format("wshshell.sendkeys \"{0}\"", password));
                builder.AppendLine("wshshell.sendkeys \"{enter}\"");
                builder.AppendLine("");
                builder.AppendLine("wscript.sleep 100");
                builder.AppendLine("wshshell.sendkeys \"{enter}\"");
                builder.AppendLine("");
                builder.AppendLine("wscript.sleep 100");
                builder.AppendLine("wshshell.sendkeys \"{enter}\"");
                builder.AppendLine("");
                builder.AppendLine("wscript.sleep 100");
                builder.AppendLine("wshshell.sendkeys \"{enter}\"");
                builder.AppendLine("");
                builder.AppendLine("wscript.sleep 100");
                builder.AppendLine("wshshell.sendkeys \"{enter}\"");
                builder.AppendLine("");
                builder.AppendLine("wscript.sleep 100");
                builder.AppendLine("wshshell.sendkeys \"{enter}\"");
                builder.AppendLine("");
                builder.AppendLine("wscript.sleep 100");
                builder.AppendLine("wshshell.sendkeys \"{enter}\"");
                builder.AppendLine("");
                builder.AppendLine("wscript.sleep 100");
                builder.AppendLine("wshshell.sendkeys \"Y\"");
                builder.AppendLine("wshshell.sendkeys \"{enter}\"");
                builder.AppendLine("");
                builder.AppendLine("wscript.sleep 1000");
                builder.AppendLine("wshshell.sendkeys \"{enter}\"");
                builder.AppendLine("");
                builder.AppendLine("wscript.sleep 1000");
                builder.AppendLine("wshshell.sendkeys \"exit\"");
                builder.AppendLine("wshshell.sendkeys \"{enter}\"");
                string batPath = Path.GetDirectoryName(folder) + "/keystore.vbs";
                File.WriteAllText(batPath, builder.ToString(), Encoding.Default);
                Process zProcInfo = new Process();
                zProcInfo.StartInfo.WorkingDirectory = Path.GetDirectoryName(folder);
                zProcInfo.StartInfo.FileName = batPath;
                zProcInfo.StartInfo.CreateNoWindow = false;
                zProcInfo.Start();
                zProcInfo.WaitForExit();
                File.Delete(batPath);
                Debug.Log("生成安卓证书 keystorePath = " + keystorePath + " , keyaliasName = " + keyaliasName + " , password = " + password);
                return File.Exists(keystorePath);
            }

            //=============================================================================================================

            [Serializable]
            private class OutputConfig
            {
                public ulong channel = 0;
                public ulong package = 0;

                public long ticks = 0;
                public string time = string.Empty;

                public OutputConfig(ulong channel, ulong package)
                {
                    this.channel = channel;
                    this.package = package;

                    DateTime dateTime = DateTime.Now;
                    ticks = dateTime.Ticks;
                    time = dateTime.ToString("F");
                }
            }
        }
    }
}
